using System;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Data;

namespace ReactiveLang.State
{
    internal class DependencyPropertyExpressionActivation : DependencyObject, IDisposable
    {
        IExpressionParent<object> _parent;
        DependencyObject _instance;
        DependencyProperty _property;

        public DependencyPropertyExpressionActivation(DependencyObject instance, DependencyProperty property, IExpressionParent<object> parent)
        {
            Contract.Requires<ArgumentNullException>(instance != null, "instance");
            Contract.Requires<ArgumentNullException>(property != null, "property");
            Contract.Requires<ArgumentNullException>(parent != null, "parent");
            _parent = parent;
            _instance = instance;
            _property = property;
            bool isAttached = instance.GetType().GetProperty(property.Name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) == null;
            string path = isAttached ? string.Format("({0}.{1})", property.OwnerType.Name, property.Name) : property.Name;
            BindingOperations.SetBinding(this, ValueProperty, new Binding { Source = instance, Path = new PropertyPath(path) });
            parent.OnValue(instance.GetValue(property));
        }
        public void Dispose()
        {
            _parent = null;
            Action clear = delegate
            {
                BindingOperations.ClearBinding(this, ValueProperty);
            };
            if (Dispatcher.Thread == System.Threading.Thread.CurrentThread)
                clear();
            else
                Dispatcher.BeginInvoke(clear);
        }

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(DependencyPropertyExpressionActivation),
            new PropertyMetadata(OnChanged));

        static void OnChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var mediator = (DependencyPropertyExpressionActivation)obj;
            if (mediator._parent != null)
                mediator._parent.OnValue(e.NewValue);
        }
    }
}